package com.yvon.core.page;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare;
import com.baomidou.mybatisplus.core.conditions.interfaces.Func;
import com.baomidou.mybatisplus.core.conditions.interfaces.Join;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.QueryChainWrapper;
import com.google.common.base.CaseFormat;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class SearchCriteria extends BaseSearchCriteria {
    private static final long serialVersionUID = -8630867858577408179L;
    private static final String IN_SPLIT = ",";

    public SearchCriteria() {
    }

    public <T> Page<T> buildPage() {
        Page<T> page = new Page(this.getPage(), this.getSize());
        if (CollUtil.isNotEmpty(this.getOrderParams())) {
            this.getOrderParams().forEach((orderParam) -> {
                if (!Strings.isNullOrEmpty(orderParam.getFieldName())) {
                    String fieldName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, orderParam.getFieldName());
                    if (OrderParam.Order.desc == orderParam.getOrder()) {
                        page.addOrder(new OrderItem[]{OrderItem.desc(fieldName)});
                    } else {
                        page.addOrder(new OrderItem[]{OrderItem.asc(fieldName)});
                    }
                }

            });
        }

        return page;
    }

    public <T> QueryWrapper<T> buildQuery(QueryWrapper<T> query) {
        Set<SearchParam> searchParams = this.getSearchParams();
        if (null != searchParams && searchParams.size() > 0) {
            searchParams.forEach((searchParam) -> {
                if (!Strings.isNullOrEmpty(searchParam.getFieldName())) {
                    this.queryFilterChain(query, searchParam);
                }

            });
        }

        return query;
    }

    public <T> QueryChainWrapper<T> buildQuery(QueryChainWrapper<T> query) {
        Set<SearchParam> searchParams = this.getSearchParams();
        if (null != searchParams && searchParams.size() > 0) {
            searchParams.forEach((searchParam) -> {
                if (!Strings.isNullOrEmpty(searchParam.getFieldName())) {
                    this.queryFilterChain(query, searchParam);
                }

            });
        }

        return query;
    }

    private void queryFilterChain(QueryChainWrapper query, SearchParam searchParam) {

        searchParam.setFieldName(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, searchParam.getFieldName()));
        this.doQueryFilterChain(this.doCompareFilter(query, searchParam), this.doFuncFilter(query, searchParam), this.doJoinFilter(query, searchParam), Boolean.FALSE);
    }

    private void queryFilterChain(QueryWrapper query, SearchParam searchParam) {
        searchParam.setFieldName(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, searchParam.getFieldName()));
        this.doQueryFilterChain(this.doCompareFilter(query, searchParam), this.doFuncFilter(query, searchParam), this.doJoinFilter(query, searchParam), Boolean.FALSE);
    }

    private void doQueryFilterChain(Boolean compare, Boolean func, Boolean join, Boolean nested) {
        if (!compare) {
            if (!func) {
                if (!join) {
                    if (!nested) {
                        ;
                    }
                }
            }
        }
    }

    private <T> Boolean doJoinFilter(Join<T> query, SearchParam searchParam) {
        String endDate;
        switch(searchParam.getOperate()) {
        case between:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                endDate = searchParam.getValue();
                query.apply("date_format(" + searchParam.getFieldName() + ",'" + getMySqlDateFormatByTime(endDate) + "') >= {0}", new Object[]{endDate});
            }

            if (!Strings.isNullOrEmpty(searchParam.getValue1())) {
                endDate = searchParam.getValue1();
                query.apply("date_format(" + searchParam.getFieldName() + ",'" + getMySqlDateFormatByTime(endDate) + "') <= {0}", new Object[]{endDate});
            }
            break;
        case notBetween:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                endDate = searchParam.getValue();
                query.apply("date_format(" + searchParam.getFieldName() + ",'" + getMySqlDateFormatByTime(endDate) + "') <= {0}", new Object[]{endDate});
            }

            if (!Strings.isNullOrEmpty(searchParam.getValue1())) {
                endDate = searchParam.getValue1();
                query.apply("date_format(" + searchParam.getFieldName() + ",'" + getMySqlDateFormatByTime(endDate) + "') >= {0}", new Object[]{endDate});
            }
            break;
        default:
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    private <T> Boolean doFuncFilter(Func<T, String> query, SearchParam searchParam) {
        switch(searchParam.getOperate()) {
        case in:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.in(searchParam.getFieldName(), this.stringToList(searchParam.getValue(), ","));
            }
            break;
        case notIn:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.notIn(searchParam.getFieldName(), this.stringToList(searchParam.getValue(), ","));
            }
            break;
            case isNull:
                if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                    query.isNull(searchParam.getFieldName());
                }
            break;
            case isNotNull:
                if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                    query.isNotNull(searchParam.getFieldName());
                }
            break;
        default:
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    private <T> Boolean doCompareFilter(Compare<T, String> query, SearchParam searchParam) {
        switch(searchParam.getOperate()) {
        case eq:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.eq(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case ne:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.ne(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case gt:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.gt(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case ge:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.ge(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case lt:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.lt(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case le:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.le(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case like:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.like(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        case notLike:
            if (!Strings.isNullOrEmpty(searchParam.getValue())) {
                query.notLike(searchParam.getFieldName(), searchParam.getValue());
            }
            break;
        default:
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    private List<String> stringToList(@NonNull String toSplit, @Nullable String delimiter) {
        if (Strings.isNullOrEmpty(toSplit)) {
            throw new IllegalArgumentException("toSplit can not be null or empty!");
        } else {
            if (Strings.isNullOrEmpty(delimiter)) {
                delimiter = ",";
            }

            ArrayList searchValue;
            if (!toSplit.contains(delimiter)) {
                searchValue = Lists.newArrayList(new String[]{toSplit});
            } else {
                searchValue = Lists.newArrayList(Lists.newArrayList(toSplit.split(delimiter)));
            }

            return searchValue;
        }
    }

    public String getMySqlDateFormatByTime(String time) {
        if (time.matches("^\\d{4}-\\d{1,2}$")) {
            return "%Y-%m";
        } else if (time.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
            return "%Y-%m-%d";
        } else if (time.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
            return "%Y-%m-%d %H:%i";
        } else if (time.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
            return "%Y-%m-%d %H:%i:%S";
        } else {
            throw new InvalidParameterException("时间格式不正确：" + time);
        }
    }
}
